<template>
    <!-- text-sm/[30px] 表示文字小号，行高为 30px -->
    <div
        class="sticky top-[5.5rem] text-sm/[30px] w-full p-5 mb-3 bg-white border border-gray-200 rounded-lg dark:bg-gray-800 dark:border-gray-700">
        <!-- 目录标题 -->
        <h2 class="flex items-center mb-2 font-bold text-gray-900 uppercase dark:text-white text-lg">
            <!-- 目录图标 -->
            <svg t="1730390621771" class="icon w-5 h-5 mr-2" viewBox="0 0 1024 1024" version="1.1"
                xmlns="http://www.w3.org/2000/svg" p-id="59702" width="200" height="200">
                <path
                    d="M171.8272 0c-14.455467 0-27.767467 2.525867-39.714133 7.697067a97.4336 97.4336 0 0 0-32.546134 23.0912A115.063467 115.063467 0 0 0 76.6976 66.730667 108.066133 108.066133 0 0 0 68.266667 107.861333V916.138667c0 13.653333 2.850133 27.3408 8.413866 41.130666a115.063467 115.063467 0 0 0 22.869334 35.9424c9.693867 10.24 20.497067 17.92 32.5632 23.0912 12.066133 5.1712 25.258667 7.697067 39.714133 7.697067h680.3456c14.455467 0 27.767467-2.525867 39.714133-7.697067a97.4336 97.4336 0 0 0 32.546134-23.0912 115.063467 115.063467 0 0 0 22.869333-35.959466A108.066133 108.066133 0 0 0 955.733333 916.138667V307.6608L670.532267 0H171.844267z"
                    fill="#CCE7FF" p-id="59703"></path>
                <path
                    d="M713.4208 307.2c-27.374933 0.341333-48.810667-21.6064-47.786667-50.2784L674.5088 0 955.733333 304.6912 713.4208 307.2z"
                    fill="#2C8BFD" p-id="59704"></path>
                <path
                    d="M221.866667 341.333333m-34.133334 0a34.133333 34.133333 0 1 0 68.266667 0 34.133333 34.133333 0 1 0-68.266667 0Z"
                    fill="#2C8BFD" p-id="59705"></path>
                <path
                    d="M327.816533 307.2m34.133334 0l218.3168 0q34.133333 0 34.133333 34.133333l0 0q0 34.133333-34.133333 34.133334l-218.3168 0q-34.133333 0-34.133334-34.133334l0 0q0-34.133333 34.133334-34.133333Z"
                    fill="#2C8BFD" p-id="59706"></path>
                <path
                    d="M221.866667 580.266667m-34.133334 0a34.133333 34.133333 0 1 0 68.266667 0 34.133333 34.133333 0 1 0-68.266667 0Z"
                    fill="#2C8BFD" p-id="59707"></path>
                <path
                    d="M326.1952 546.133333m34.133333 0l441.8048 0q34.133333 0 34.133334 34.133334l0 0q0 34.133333-34.133334 34.133333l-441.8048 0q-34.133333 0-34.133333-34.133333l0 0q0-34.133333 34.133333-34.133334Z"
                    fill="#2C8BFD" p-id="59708"></path>
                <path
                    d="M221.866667 819.2m-34.133334 0a34.133333 34.133333 0 1 0 68.266667 0 34.133333 34.133333 0 1 0-68.266667 0Z"
                    fill="#2C8BFD" p-id="59709"></path>
                <path
                    d="M326.741333 785.066667m34.133334 0l441.258666 0q34.133333 0 34.133334 34.133333l0 0q0 34.133333-34.133334 34.133333l-441.258666 0q-34.133333 0-34.133334-34.133333l0 0q0-34.133333 34.133334-34.133333Z"
                    fill="#2C8BFD" p-id="59710"></path>
            </svg>
            文章目录
        </h2>

        <div class="toc-wrapper" :class="[isDark ? 'dark' : '']">
            <ul class="toc">
                <!-- 一级标题 -->
                <li v-for="(h1, index1) in titles" :key="index1">
                    <span v-if="h1.text && h1.text != ''"
                    class="py-2 pl-5 text-sm hover:text-sky-600" 
                    :class="[h1.index == activeHeadingIndex ? 'active py-1 text-sky-600 border-l-2 border-sky-600 font-bold' : 'text-gray-500 font-normal']"
                    @click="scrollToView(h1.offsetTop)">{{ h1.text }}</span>
                    <!-- 二级标题 -->
                    <ul v-if="h1.children && h1.children.length > 0">
                        <li v-for="(h2, index2) in h1.children" :key="index2">
                            <span v-if="h2.text && h2.text != ''"
                            class="pl-10 text-sm hover:text-sky-600" 
                            :class="[h2.index == activeHeadingIndex ? 'active py-1 text-sky-600 border-l-2 border-sky-600 font-bold' : 'text-gray-500 font-normal']"
                            @click="scrollToView(h2.offsetTop)">{{ h2.text }}</span>
                            <!-- 三级标题 -->
                            <ul v-if="h2.children && h2.children.length > 0">
                                <li v-for="(h3, index3) in h2.children" :key="index3">
                                    <span v-if="h3.text && h3.text != ''"
                                    class="pl-20 text-sm hover:text-sky-600" 
                                    :class="[h3.index == activeHeadingIndex ? 'active py-1 text-sky-600 border-l-2 border-sky-600 font-bold' : 'text-gray-500 font-normal']"
                                    @click="scrollToView(h3.offsetTop)">{{ h3.text }}</span>
                                </li>
                            </ul>
                        </li>
                    </ul>
                </li>
            </ul>
        </div>
    </div>
</template>

<script setup>
import { onMounted, onBeforeUnmount } from 'vue';
import { ref } from 'vue';
import { useDark } from '@vueuse/core';
const titles = ref([])
// 添加dark
const isDark = useDark()

// 提取标题
onMounted(() => {
    //通过.article-content 获取父级元素
    const container  = document.querySelector('.article-content')
    // 使用MutationObserver 来监视DOM变化
    const observer = new MutationObserver(mutationsList => {
        for (let mutation of mutationsList) {
            if (mutation.type == 'childList') {
                titles.value = []
                initTocData(container)

                // 监听图片加载事件
                const images = container.querySelectorAll('img')
                images.forEach(img => {
                    img.addEventListener('load', () => {
                        // 图片加载完全后开始计算目录位置
                        initTocData(container)
                    })
                })

                // 添加滚动事件监听
                window.addEventListener('scroll', handleContentScroll);
            }
        }
    })

    //配置监听子节点变化
    const config = {childList: true, subtree: true}
    observer.observe(container, config)
})
// 考虑header偏移
const headOffset = ref(100)
// 封装目录生成与位置生成代码
const initTocData = (container) => {
    // 提取h1, h2, h3级标题
    let levels = ['h1', 'h2', 'h3']
    let headings = container.querySelectorAll(levels)
    //console.log(headings)

    // 存放组装后的标题数据
    let titleArr = []

    let index = 1
    headings.forEach(heading => {
        // 标题登记 1 2 3
        let headingLevel = parseInt(heading.tagName.substring(1))
        // 标题文字
        let headingText = heading.innerText
        // 标题的位置
        let offsetTop = heading.offsetTop - headOffset.value
        if (headingLevel == 1) {
            // 一级标题
            titleArr.push({
                index,
                level: headingLevel,
                text: headingText,
                offsetTop,
                children: [] // 一级标题下的子标题
            })
            index++
        } else if (headingLevel == 2) {
            // 父级标题
            let parentHeading = titleArr[titleArr.length - 1]
            if (!parentHeading) {
                // 创建一级标题
                titleArr.push({
                    index,
                    level: 1,
                    text: '',
                    offsetTop,
                    children: [] // 二级标题下的子标题
                })
                parentHeading = titleArr[titleArr.length - 1]
                index++
            }
            // 将二级标题存入父标题下
            parentHeading.children.push({
                index,
                level: headingLevel,
                text: headingText,
                offsetTop,
                children: [] // 二级标题下的子标题
            })
            index++
        } else if (headingLevel == 3) {
            // 一级父级标题
            let grandParentHeading = titleArr[titleArr.length - 1]
            if (!grandParentHeading) {
                // 创建一级标题
                titleArr.push({
                    index,
                    level: 1,
                    text: '',
                    offsetTop,
                    children: []
                })
                index++
                grandParentHeading = titleArr[titleArr.length - 1]
            } 
            // 二级父级标题
            let parentHeading = grandParentHeading.children[grandParentHeading.children.length - 1]
            if (!parentHeading) {
                // 创建二级标题
                grandParentHeading.children.push({
                    index,
                    level: 2,
                    text: '',
                    offsetTop,
                    children: []
                })
                index++
                parentHeading = grandParentHeading.children[grandParentHeading.children.length - 1]
            }
            
            // 添加三级标题
            parentHeading.children.push({
                index,
                level: headingLevel,
                text: headingText,
                offsetTop,
                children: [] // 三级标题下的子标题
            })
            index++
        }
    })
    // console.log(titleArr)
    // 设置数据
    titles.value = titleArr
}

// 记录当前被选中的标题下标
const activeHeadingIndex = ref(-1)
// 处理滚动事件
const handleContentScroll = () => {
    // 当前位置
    let scrollY = window.scrollY
    // 循环目录
    titles.value.forEach(title => {
        // 一级标题滚动监听
        titleListening(title, scrollY, headOffset.value)
        // 二级标题滚动监听
        let children = title.children
        if (children && children.length > 0) {
            children.forEach((child) => {
                titleListening(child, scrollY, headOffset.value)
                // 三级标题滚动监听
                let grandChildren = child.children
                if (grandChildren && grandChildren.length > 0) {
                    grandChildren.forEach((grandChild) => {
                        titleListening(grandChild, scrollY, headOffset.value)
                    })
                }
            })
        }
    })
}

// 标题滚动监听
const titleListening = (titleNode, scrollY, deviation) => {
    // 判断标题结节情况
    if (!titleNode) return

    // 获取标题位置
    let offsetTop = titleNode.offsetTop
    // 判断与当前位置关系 deviation为误差值
    if (scrollY >= offsetTop - deviation) {
        // 标记选中
        activeHeadingIndex.value = titleNode.index
    }
}

// 滚动到指定的位置
function scrollToView(offsetTop) {
    window.scrollTo({ top: offsetTop, behavior: "smooth" });
}

// 移除滚动监听
onBeforeUnmount(() => window.removeEventListener('scroll', handleContentScroll))
</script>


<style scoped>
::v-deep(.toc-wrapper) {
    position: relative;
    overflow-x: hidden;
    overflow-y: hidden;
    /* max-height: 75vh; */
    text-overflow: ellipsis;
    white-space: nowrap;
    scroll-behavior: smooth;
}

::v-deep(.toc:before) {
    content: " ";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: -1;
    width: 2px;
    background: #eaecef;
}

/* 侧边栏dark样式 */
::v-deep(.dark .toc:before) {
    content: " ";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    z-index: -1;
    width: 2px;
    background: #30363d;
}

::v-deep(.dark .toc li span) {
    color: #9e9e9e;
}

::v-deep(.dark .toc li .active) {
    color: rgb(2 132 199 / 1);
}

::v-deep(.dark .toc li span:hover) {
    color: rgb(2 132 199 / 1);
}
</style>